package net.bingosoft.common.struts.tags;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.jsp.JspException;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.taglib.TagUtils;
import org.apache.struts.taglib.html.Constants;
import org.apache.struts.taglib.html.OptionsCollectionTag;
import org.apache.struts.taglib.html.SelectTag;
import org.apache.struts.util.LabelValueBean;

/**
 * 2007-8-13 上午10:33:48
 * Author:yangdongyu(yangdy@bingosoft.net)
 * ReadMe:OptionsCollectionTag 标签的改写,提供对下拉框链动的支持
 * ChangeLog:版本初建
 */
public class StrutsOptionsCollectionTag extends OptionsCollectionTag {

	private static final long serialVersionUID = 1L;
	private String belongTo ;

	//扩展属性
	private String extend;
	/**
	 * @return the extend
	 */
	public String getExtend() {
		return extend;
	}
	/**
	 * @param extend the extend to set
	 */
	public void setExtend(String extend) {
		this.extend = extend;
	}
	
	public String getBelongTo() {
		return belongTo;
	}
	public void setBelongTo(String belong) {
		this.belongTo = belong;
	}
	/**
	 * Process the start of this tag.
	 *
	 * @exception JspException if a JSP exception has occurred
	 */
	public int doStartTag() throws JspException {

		// Acquire the select tag we are associated with
		SelectTag selectTag = (SelectTag) pageContext.getAttribute(Constants.SELECT_KEY);

		if (selectTag == null) {
			JspException e = new JspException(messages.getMessage("optionsCollectionTag.select"));
			TagUtils.getInstance().saveException(pageContext, e);
			throw e;
		}

		// Acquire the collection containing our options
		Object collection = TagUtils.getInstance().lookup(pageContext, name, property, null);
		if (collection == null) {
			JspException e =
				new JspException(messages.getMessage("optionsCollectionTag.collection"));
			TagUtils.getInstance().saveException(pageContext, e);
			throw e;
		}
		// Acquire an iterator over the options collection
		Iterator iter = getIterator(collection);
		StringBuffer sb = new StringBuffer();
		// Render the options
		while (iter.hasNext()) {
			Object bean = iter.next();
            if(bean instanceof Map.Entry){
            	this.value = this.value == null ? "key":this.value;
            	this.label = this.label == null ? "value":this.label;
            }else if(bean instanceof LabelValueBean){
            	this.value = this.value == null ? "value":this.value;
            	this.label = this.label == null ? "label":this.label;
            }
			// Get the label for this option
			Object beanLabel = getProperty(bean, label);
			// Get the value for this option
			Object beanValue = getProperty(bean, value);
			//-----------------------------------------------------------------------------
			// Get the belongTo for this option
			Object belongToValue = null;
			if(StringUtils.isNotEmpty(belongTo)){
				belongToValue = getProperty(bean, belongTo);
			}
			belongToValue = belongToValue == null ? "":belongToValue;
			//----------------------------------------------------------------------------
			// Get the belongTo for this option
			Object extendValue = null;
			if(StringUtils.isNotEmpty(extend)){
				extendValue = getProperty(bean, extend);
			}
			extendValue = extendValue == null ? "":extendValue;
			//----------------------------------------------------------------------------
			String stringLabel = beanLabel.toString();
			String stringValue = beanValue.toString();
			//-----------------------------------------------------------------------------
			// Render this option
			this.addOption(sb, stringLabel, stringValue, String.valueOf(belongToValue),String.valueOf(extendValue), selectTag.isMatched(stringValue));
		}
		TagUtils.getInstance().write(pageContext, sb.toString());
		return SKIP_BODY;
	}
	/**
	 * Add an option element to the specified StringBuffer based on the
	 * specified parameters.
	 *<p>
	 * Note that this tag specifically does not support the
	 * <code>styleId</code> tag attribute, which causes the HTML
	 * <code>id</code> attribute to be emitted.  This is because the HTML
	 * specification states that all "id" attributes in a document have to be
	 * unique.  This tag will likely generate more than one <code>option</code>
	 * element element, but it cannot use the same <code>id</code> value.  It's
	 * conceivable some sort of mechanism to supply an array of <code>id</code>
	 * values could be devised, but that doesn't seem to be worth the trouble.
	 *
	 * @param sb StringBuffer accumulating our results
	 * @param value Value to be returned to the server for this option
	 * @param label Value to be shown to the user for this option
	 * @param matched Should this value be marked as selected?
	 */
	protected void addOption(StringBuffer sb, String label, String value, String belongToValue,String extendValue, boolean matched) {

		sb.append("<option value=\"");
		if (filter) {
			sb.append(TagUtils.getInstance().filter(value));
		} else {
			sb.append(value);
		}
		sb.append("\"");
		if (matched) {
			sb.append(" selected=\"selected\"");
		}
		if (getStyle() != null) {
			sb.append(" style=\"");
			sb.append(getStyle());
			sb.append("\"");
		}
		if (getStyleClass() != null) {
			sb.append(" class=\"");
			sb.append(getStyleClass());
			sb.append("\"");
		}
		if (getStyleClass() != null) {
			sb.append(" class=\"");
			sb.append(getStyleClass());
			sb.append("\"");
		}
		// add belongTo property
		if (!belongToValue.equals("")) {
			sb.append(" belongTo=\"");
			sb.append(belongToValue);
			sb.append("\"");
		}
		//add exntend property
		if (!extendValue.equals("")) {
			sb.append(" extend=\"");
			sb.append(extendValue);
			sb.append("\"");
		}
		sb.append(">");

		if (filter) {
			sb.append(TagUtils.getInstance().filter(label));
		} else {
			sb.append(label);
		}

		sb.append("</option>\r\n");

	}

	/**
	 * 获取bean中的属性
	 * @param bean 
	 * @param property
	 * @return
	 * @throws JspException
	 */
	private Object getProperty(Object bean,String property) throws JspException{
		Object result ;		
		try {
			result = PropertyUtils.getProperty(bean, property);
			if (result == null) {
				result = "";
			}
		} catch (IllegalAccessException e) {
			JspException jspe =
				new JspException(messages.getMessage("getter.access", label, bean));
			TagUtils.getInstance().saveException(pageContext, jspe);
			throw jspe;
		} catch (InvocationTargetException e) {
			Throwable t = e.getTargetException();
			JspException jspe =
				new JspException(messages.getMessage("getter.result", label, t.toString()));
			TagUtils.getInstance().saveException(pageContext, jspe);
			throw jspe;
		} catch (NoSuchMethodException e) {
			JspException jspe =
				new JspException(messages.getMessage("getter.method", label, bean));
			TagUtils.getInstance().saveException(pageContext, jspe);
			throw jspe;
		}
		return result;
	}
}
